﻿/*
 Copyright (c) 2009 Alvin Cho (alvincho@risklib.com)
 Copyright (c) 2009 RiskLib.com
 
 This file is part of RiskLib.NET Project. See http://www.risklib.net for more information.
 
 RiskLib.NET is an open source project. Using of RiskLib.NET is free of charge.
 It is distributed under GNU General Public License version 2 (GPLv2).
 GPLv2 can be found at http://www.risklib.net/risklib/license.
 
 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Data.SqlClient;
using System.Data.SqlServerCe;
using System.Text.RegularExpressions;

namespace RiskLib.DataSource
{
    public interface IHistoricalBase
    {
        FieldMappingList HistoricalFields { get; set; }
        event HistoricalDataEventHandler HistoricalDataEvent;
        void GotHistoricalData(HistoricalDataEventArgs args);
    }
    public delegate void HistoricalDataEventHandler(object sender, HistoricalDataEventArgs e);
    public interface IHistoricalSource:IHistoricalBase
    {
        HistoricalDataList GetHistoricalData(IProduct product, DateTime startDate, DateTime endDate);
        HistoricalDataList GetHistoricalData(IProduct product, List<FieldNameEnum> fieldList, DateTime startDate, DateTime endDate);
        HistoricalDataList GetHistoricalData(string productCode, List<FieldNameEnum> fieldList, DateTime startDate, DateTime endDate);
    }
    public interface IDailyClosingSource:IHistoricalBase
    {
        DailyClosingList GetDailyClosing(DateTime date);
    }
    public delegate void FundamentalDataEventHandler(object sender, FundamentalDataEventArgs e);
    public interface IFundamentalSource
    {
        event FundamentalDataEventHandler FundamentalDataEvent;

        FieldMappingList FundamentalFields { get; set; }
        FundamentalDataList GetFundamentalData(IProduct product);
        FundamentalDataList GetFundamentalData(IProduct product, List<FieldNameEnum> fieldList);
        void GotFundamentalData(FundamentalDataEventArgs args);
    }
    public interface IFundamentalDestination
    {
        int SaveFundamentalData(IProductCode productCode, FundamentalDataList fdList);
    }

    public interface IHistoricalDestination
    {
        int SaveHistoricalData(IProductCode productCode, HistoricalDataList hdList);
        int SaveHistoricalData(IProductCode productCode, HistoricalData hData);
    }
    public delegate void RealTimeSourceEventHandler(object sender, RealTimeDataEventArgs e);
    public interface IRealTimeSource
    {
        FieldMappingList RealTimeFields { get; set; }

        event RealTimeSourceEventHandler RealTimeDataEvent;

        bool Subscribe(IProductCode code, List<FieldNameEnum> fields);
        void GotRealTimeData(RealTimeDataEventArgs args);
    }
    public interface IProductListSource
    {
        List<AbstractProduct> GetProductList();
    }
    public interface IProductCodeData
    {
        List<IProductCode> GetProductCodeList();
        List<IProductCode> GetProductCodeList(string partialCode);
    }
    public class DataSourceEventArgs : EventArgs
    {
        public DataSourceEventArgs() { }
        public DataSourceEventArgs(string msg)
        {
            Message = msg;
        }
        public string Message;
    }
    public class FundamentalDataEventArgs : DataSourceEventArgs
    {
        public FundamentalDataEventArgs(FundamentalDataList productFieldDatas, string message)
            : base(message)
        {
            fieldDatas = productFieldDatas;
        }
        public IProductCode productCode;
        public FundamentalDataList fieldDatas;
    }

    public class RealTimeDataEventArgs : DataSourceEventArgs
    {
        public IProductCode productCode;
        //public DataSourceEnum dataSource;
        public FieldNameEnum fieldName;
        public double value;
    }
    public class HistoricalDataEventArgs : DataSourceEventArgs
    {
        public IProductCode product;
        public HistoricalData Data;
    }
    public abstract class AbstractDataSource
    {
        //Dictionary<IProductCode, HistoricalDataList> historicalCache = new Dictionary<IProductCode, HistoricalDataList>();

        public AbstractDataSource(string name)
        {
            dsName = name;
        }

        private string dsName;

        public string Name
        {
            get { return dsName; }
        }

        public event StatusEventHandler StatusEvent;
        public delegate void StatusEventHandler(object sender, DataSourceEventArgs e);
        protected void SendMessage(string msg)
        {
            if (StatusEvent != null)
            {
                DataSourceEventArgs args = new DataSourceEventArgs(msg);

                StatusEvent(this, args);
            }
        }
    }

    public abstract class FileDataSource : AbstractDataSource
    {
        public FileDataSource(string name) : base(name) { }
    }

    public abstract class TextDataSource : FileDataSource
    {
        public TextDataSource(string name) : base(name) { }
        protected string GetWebPage(string url)
        {
            return GetWebPage(url, "");
        }

        protected string GetWebPage(string url, string cookies)
        {
            int retry = 3;
            bool isFinished = false;
            bool isStopping = false;
            while (retry-- > 0)
            {
                try
                {
                    WebClient web = new WebClient();
                    if (cookies.Length > 0)
                    {
                        web.Headers.Add("Cookie", cookies);
                        web.Headers.Add("Accept", "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*");
                        web.Headers.Add("Accept-Encoding", "gzip, deflate");
                        web.Headers.Add("Accept-Language", "en-us");
                        web.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 2.0.50727)");
                    }
                    while (!isFinished & !isStopping)
                    {
                        SendMessage("Getting " + url);
                        string content = web.DownloadString(url);
                        SendMessage("Got web page " + url);
                        return content;
                    }
                }
                catch (WebException ex)
                {
                    SendMessage("GetWebPage " + ex.Message + ". " + url);
                }
                catch (Exception ex)
                {
                    SendMessage("GetWebPage " + ex.Message + ". " + url);
                }
            }
            return "";
        }
        protected abstract IProductCode GetProductCodeFromMatch(Match match);

    }
    public abstract class HistoricalTextDataSource : TextDataSource, IHistoricalSource
    {
        public HistoricalTextDataSource(string name) : base(name) { }
        #region IHistoricalSource 成員

        public virtual HistoricalDataList GetHistoricalData(IProduct product, DateTime startDate, DateTime endDate)
        {
            return GetHistoricalData(product, product.HistoricalFields, startDate, endDate);
        }

        public virtual HistoricalDataList GetHistoricalData(IProduct product, List<FieldNameEnum> fieldList, DateTime startDate, DateTime endDate)
        {
            return GetHistoricalData(product.ProductCode, fieldList, startDate, endDate);
        }

        public abstract HistoricalDataList GetHistoricalData(string productCode, List<FieldNameEnum> fieldList, DateTime startDate, DateTime endDate);

        #endregion

        #region IHistoricalBase 成員

        public abstract FieldMappingList HistoricalFields { get;set;}

        public event HistoricalDataEventHandler HistoricalDataEvent;

        public void GotHistoricalData(HistoricalDataEventArgs args)
        {
            if (HistoricalDataEvent != null)
            {
                HistoricalDataEvent(this, args);
            }
        }

        #endregion
    }
    public abstract class DailyClosingTextDataSource : TextDataSource,IDailyClosingSource
    {
        public DailyClosingTextDataSource(string name) : base(name) { }
        protected DailyClosingList ParseDailyQuoteList(DateTime d, string content, Regex regex, FieldMappingList fields)
        {
            int dataGot = 0;
            DailyClosingList hList = new DailyClosingList();
            //HistoricalData hdata = new HistoricalData();
            try
            {
                foreach (Match m in regex.Matches(content))
                {
                    HistoricalData quote = new HistoricalData();
                    quote.Date = d;
                    quote.ProductCode = GetProductCodeFromMatch(m);
                    foreach (FieldNameEnum fName in fields.Keys)
                    {
                        double v=0;
                        if (double.TryParse(m.Groups[fields[fName]].Value, out v))
                            quote.Values.Add(fName, v);
                    }
                    HistoricalDataEventArgs args=new HistoricalDataEventArgs();
                    args.product=quote.ProductCode;
                    args.Data=quote;
                    GotHistoricalData(args);
                    hList.Add(quote.ProductCode, quote);
                }
            }
            catch (Exception ex)
            {
                SendMessage("Error: "+ex.Message);
            }
            return hList;
        }

        #region IDailyClosingSource Members

        public abstract DailyClosingList GetDailyClosing(DateTime date);

        #endregion

        #region IHistoricalBase Members

        public abstract FieldMappingList HistoricalFields{get;set;}

        public event HistoricalDataEventHandler HistoricalDataEvent;

        public void GotHistoricalData(HistoricalDataEventArgs args)
        {
            if (HistoricalDataEvent != null)
            {
                HistoricalDataEvent(this, args);
            }
        }

        #endregion
    }

    public abstract class ExcelDataSource : FileDataSource
    {
        public ExcelDataSource(string name) : base(name) { }
    }

    /// <summary>
    /// This class defines a datasource in SQL Server.
    /// It should be redesigned to use a parent class defines a generic database datasource.
    /// </summary>
    public abstract class SQLCeDataSource : AbstractDataSource,IHistoricalSource,IDailyClosingSource,IFundamentalSource,IFundamentalDestination,IHistoricalDestination
    {
        public SQLCeDataSource(string name, string connString, string historicalTableName, string fundamentalTableName, FieldMappingList fundamentalFieldMapping, FieldMappingList historicalFieldMapping)
            : base(name)
        {
            connectionString = connString;
            historicalFields = historicalFieldMapping;
            fundamentalFields = fundamentalFieldMapping;
            historicalTable = historicalTableName;
            fundamentalTable = fundamentalTableName;
            cmd = GetCmd();
        }

        protected string connectionString;

        protected string sql = "";

        protected string historicalTable;
        protected string fundamentalTable;

        [System.Xml.Serialization.XmlIgnore()]
        public SqlCeCommand cmd;

        public SqlCeConnection GetConn()
        {
            SqlCeConnection conn = new SqlCeConnection(connectionString);
            conn.Open();
            return conn;
        }
        public SqlCeCommand GetCmd()
        {
            SqlCeConnection conn = GetConn();
            SqlCeCommand cmd = new SqlCeCommand();
            cmd.Connection = conn;
            return cmd;
        }

        public int ExecuteNonQuery(string sqlCmd)
        {
            cmd.CommandText = sqlCmd;
            return cmd.ExecuteNonQuery();
        }
        public object ExecuteScalar(string sqlCmd)
        {
            cmd.CommandText = sqlCmd;
            return cmd.ExecuteScalar();
        }

        private FieldMappingList historicalFields;
        private FieldMappingList fundamentalFields;

        protected virtual string GetHistoricalInsertString(HistoricalData hdata)
        {
            string sql1 = "insert into " +historicalTable +" (";
            string sql2 = ") values (";
            foreach (FieldNameEnum fName in historicalFields.Keys)
            {
                sql1 += " " + historicalFields[fName] + ",";
                switch (fName)
                {
                    case FieldNameEnum.LocalCode:
                        sql2 += "'" + hdata.ProductCode.ProductCode + "',";
                        break;
                    case FieldNameEnum.DataDate:
                        sql2 += "'" + hdata.Date.ToShortDateString() + "',";
                        break;
                    default:
                        sql2 += "'" + hdata.Values[fName] + "',";
                        break;
                }
            }
            sql1 = sql1.Substring(0, sql1.Length - 1); // remove last comma
            sql2 = sql2.Substring(0, sql2.Length - 1) + ")";
            return sql1 + sql2 ;
            
        }
        protected virtual string GetHistoricalSelectString(string productCode)
        {
            string result = "select ";
            foreach (FieldNameEnum fName in historicalFields.Keys)
            {
                result += " " + historicalFields[fName] + ",";
            }
            result = result.Substring(0, result.Length - 1); // remove last comma
            result += " from "+historicalTable+" where " + historicalFields[FieldNameEnum.LocalCode] + " = '" + productCode + "'";
            return result;
        }

        #region IHistoricalSource 成員

        public virtual HistoricalDataList GetHistoricalData(IProduct product, DateTime startDate, DateTime endDate)
        {
            return GetHistoricalData(product, product.HistoricalFields, startDate, endDate);
        }

        public virtual HistoricalDataList GetHistoricalData(IProduct product, List<FieldNameEnum> fieldList, DateTime startDate, DateTime endDate)
        {
            return GetHistoricalData(product.ProductCode, fieldList, startDate, endDate);
        }

        public virtual HistoricalDataList GetHistoricalData(string productCode, List<FieldNameEnum> fieldList, DateTime startDate, DateTime endDate)
        {
            HistoricalDataList hdList = new HistoricalDataList();
            string sql = GetHistoricalSelectString(productCode);
            SqlCeCommand cmd = GetCmd();
            cmd.CommandText = sql;
            SqlCeDataReader reader = cmd.ExecuteReader();
            while (reader.Read())
            {
                HistoricalData hData=new HistoricalData();
                hData.Date=(DateTime)reader[historicalFields[FieldNameEnum.DataDate]];
                hData.ProductCode=new SimpleProductCode(productCode);
                foreach (FieldNameEnum fName in historicalFields.Keys)
                {
                    if (reader[historicalFields[fName]]!=DBNull.Value)
                    {
                        switch (fName)
                        {
                            case FieldNameEnum.LocalCode:
                                hData.ProductCode =new SimpleProductCode( (string)reader[historicalFields[fName]]);
                                break;
                            case FieldNameEnum.DataDate:
                                hData.Date = (DateTime)reader[historicalFields[fName]];
                                break;
                            default:
                                hData.Values.Add(fName, (double)reader[historicalFields[fName]]);
                                break;
                        }
                    }
                }
                hdList.Add(hData.Date,hData);
                HistoricalDataEventArgs args = new HistoricalDataEventArgs();
                args.product = hData.ProductCode;
                args.Data = hData;
                GotHistoricalData(args);
            }
            return hdList;
        }

        #endregion

        #region IHistoricalBase 成員

        public FieldMappingList HistoricalFields
        {
            get
            {
                return historicalFields;
            }
            set
            {
                historicalFields = value;
            }
        }

        public event HistoricalDataEventHandler HistoricalDataEvent;

        public void GotHistoricalData(HistoricalDataEventArgs args)
        {
            if (HistoricalDataEvent != null)
            {
                HistoricalDataEvent(this, args);
            }
        }

        #endregion

        #region IDailyClosingSource 成員

        public DailyClosingList GetDailyClosing(DateTime date)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IFundamentalSource 成員

        public event FundamentalDataEventHandler FundamentalDataEvent;

        public FieldMappingList FundamentalFields
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public FundamentalDataList GetFundamentalData(IProduct product)
        {
            throw new NotImplementedException();
        }

        public FundamentalDataList GetFundamentalData(IProduct product, List<FieldNameEnum> fieldList)
        {
            throw new NotImplementedException();
        }

        public void GotFundamentalData(FundamentalDataEventArgs args)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IFundamentalDestination 成員

        public int SaveFundamentalData(IProductCode productCode, FundamentalDataList fdList)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IHistoricalDestination 成員

        public int SaveHistoricalData(IProductCode productCode, HistoricalDataList hdList)
        {
            int result = 0;
            SqlCeCommand cmd = GetCmd();

            foreach (DateTime date in hdList.Keys)
            {
                result += SaveHistoricalData(productCode, hdList[date]);
            }

            return result;
        }

        public int SaveHistoricalData(IProductCode productCode, HistoricalData hData)
        {
            int result = 0;
            string sql = GetHistoricalInsertString(hData);
            cmd.CommandText = sql;
            result += cmd.ExecuteNonQuery();
            SendMessage("HistoricalData Saved: "+hData.ToString());
            return result;
        }

        #endregion
    }
    public class FieldMapping
    {
        public FieldMapping(FieldNameEnum field, string mappedField)
        {
            fieldName = field;
            dataField = mappedField;
        }
        public FieldNameEnum fieldName;
        public string dataField;
    }
    public class FieldMappingList : Dictionary<FieldNameEnum, string>
    {
    }
}